//***********************************************
//Project Name               :
//File Name                  :
//Author                     :ZJL
//Date of Creation           :20190920
//Functional Description     :图像数据仿真
//              
//Revision History           :
//Change Log                 :
//***********************************************
`timescale 1ns/1ns
module tb_x2;
//------------------------------------------
// `define 1280_1024_30


// `ifdef 1280_1024_30
// 	parameter FRAME_RATE 		= 30;								//帧频
// 	parameter RES_WIDTH 		= 1280;								//分辨率：宽度
// 	parameter RES_HEIGHT 		= 1024;								//分辨率：高度
// `endif

// `else//def 320_256_50
// 	parameter FRAME_RATE 		= 50;								//帧频
// 	parameter RES_WIDTH 		= 320;								//分辨率：宽度
// 	parameter RES_HEIGHT 		= 256;								//分辨率：高度
// `endif


parameter FRAME_RATE 		= 50	;							//帧频
parameter RES_WIDTH 		= 640	;							//分辨率：宽度
// parameter RES_HEIGHT 		= 512	;							//分辨率：高度
parameter RES_HEIGHT 		= 514	;							//分辨率：高度
parameter PERIOD_50MHZ	 	= 20	;							//50MHz
parameter LINE_BLANK 		= 100	;							//消隐期周期数
parameter EXTRA_LINES 		= 10	;							//多输出的行数
parameter FRAME_PERIOD 		= 1_000_000_000/FRAME_RATE;			//帧周期-‭16,666,666.66666667‬ns
parameter TOTAL_PIXEL 		= (RES_WIDTH + LINE_BLANK);			//多输出的行数-1330
parameter TOTAL_LINES 		= (RES_HEIGHT + EXTRA_LINES);		//多输出的行数-1040
parameter TEMP1 			= FRAME_PERIOD/20;					//多输出的行数-1040
parameter TEMP2 			= TOTAL_PIXEL * TOTAL_LINES;		//多输出的行数-1040
parameter TEMP3 			= TEMP1 - TEMP2;					//多输出的行数-1040
parameter CNT_WAIT_TIME 	= (FRAME_PERIOD/20 - (TOTAL_PIXEL * TOTAL_LINES))/2;//多输出的行数
//信号列表
reg	 				clk;		//clock
reg 				rst_n;		//reset @high voltage

reg					in_pulse;	//input signal
reg					i_start;	//to streamscale
wire 	[15:0]		o_data;		//data after scale
wire 				o_dvalid;	//data valid after scale
wire 	[15:0]		data_tmp;
wire 	[15:0]		data_tmp2;
integer 			i;			//counter for random task
reg 	[15:0]		rand_data1;	//random data out
reg 	[15:0]		rand_data2;	//random data out
integer 			N = 512;		//random seed
reg 	[15:0] 		reg_mem[0:RES_WIDTH*RES_HEIGHT-1];//={0};	//! memory
integer 			addr;				//memory address

reg 	[1-1:0] 		field_rst;
wire 	[1-1:0] 		wait_done;
reg 	[1-1:0] 		flag_image;

wire        [1-1:0]         	wait_time_add		;
reg        	[1-1:0]         	en_cnt_wait_time	;
reg        	[32-1:0]        	cnt_wait_time		;
reg 		[12-1:0] 			hcnt				;
reg 		[12-1:0] 			vcnt				;
wire 		[1-1:0] 			h_valid				;
wire 		[1-1:0] 			v_valid				;
//------------------------------------------

//系统时钟
initial
begin
	clk = 0;
	forever	#(PERIOD_50MHZ/2)
	clk = ~clk;
end

//------------------任务------------------------
//*任务：系统初始化
task task_sysinit;
begin
	in_pulse = 0;
	i_start = 0;
	field_rst = 0;
end
endtask

//*任务：Generate global reset
task task_reset;
begin
	rst_n = 0;
	repeat(2) @(negedge clk);
	rst_n = 1;
end
endtask

//产生帧复位信号
task task_field_rst;
begin
	@(posedge clk);
		field_rst = 1;
	@(posedge clk);
		field_rst = 0;
	#(FRAME_PERIOD);
	@(posedge clk);
		field_rst = 1;
	@(posedge clk);
		field_rst = 0;
end
endtask




//task of generate random bit signal
//*任务：产生随机数
task task_rand_bit;
begin
	begin
		for( i = 0; i < 255; i=i+1 )begin
			@( posedge clk );
				rand_data1 = { $random } % N;//随机数取值范围[0,N-1]
				rand_data2 = { $random } % N;//随机数取值范围[0,N-1]
		end
	end
end
endtask

//*任务：读取文件到内存
task load_data2mem;
begin
	$readmemh("./../data/lena.txt",reg_mem);
end
endtask
//-----------------存储器地址------------------------
//生成存储器地址
always @ (posedge clk)begin
	if(!rst_n) begin
		addr <= 0;
	end
	else if( h_valid )begin
		addr <= addr +1;
	end
end
assign data_tmp = h_valid?(reg_mem[addr]):0;//从内存中读出数据
assign data_tmp2 = { data_tmp[7:0],data_tmp[15:8] };//从内存中读出数据
//------------------行计数-----------------------
assign     wait_time_add = field_rst;
always @ ( posedge clk ) begin
	if( ~rst_n ) begin
		cnt_wait_time        <=        'd0;
	end
	else if( en_cnt_wait_time )begin//只有在有效才计数
		cnt_wait_time        <=        cnt_wait_time + 'd1;
	end
	else begin
		cnt_wait_time        <=        'd0;
	end
end
		
always @ ( posedge clk ) begin
	if( ~rst_n ) begin
		en_cnt_wait_time        <=        'd0;
	end
	else if( cnt_wait_time == CNT_WAIT_TIME - 1)begin//结束计数条件
		en_cnt_wait_time        <=        'd0;
	end
	else if( wait_time_add )begin        //开始计数条件
		en_cnt_wait_time        <=        'd1;
	end
	else begin
		en_cnt_wait_time        <=        en_cnt_wait_time;
	end
end
assign wait_done = ( cnt_wait_time == CNT_WAIT_TIME - 1);

always @ ( posedge clk ) begin
	if( ~rst_n ) begin
		flag_image        <=        'd0;
	end
	else if( vcnt == RES_HEIGHT-1 && hcnt == RES_WIDTH-1 )begin//结束计数条件
		flag_image        <=        'd0;
	end
	else if( wait_done )begin        //开始计数条件
		flag_image        <=        'd1;
	end
	else begin
		flag_image        <=        flag_image;
	end
end
always @ ( posedge clk ) begin
	if( ~rst_n ) begin
		hcnt        <=        'd0;
	end
	else if( flag_image )begin//结束计数条件
		if( hcnt ==  TOTAL_PIXEL-1)
			hcnt        <=        'd0;
		else
			hcnt        <=        hcnt + 'd1;
	end
	else begin
		hcnt        <=        'd0;
	end
end
assign h_valid = ( flag_image && hcnt<RES_WIDTH );
assign v_valid = ( flag_image && vcnt<RES_HEIGHT );
always @ ( posedge clk ) begin
	if( ~rst_n ) begin
		vcnt        <=        'd0;
	end
	else if( flag_image )begin//结束计数条件
		if( hcnt ==  TOTAL_PIXEL-1)
			vcnt        <=        vcnt + 'd1;
		else
			vcnt        <=        vcnt;
	end
	else begin
		vcnt        <=        'd0;
	end
end



//----------------------系统初始化------------------------
initial
begin
	task_sysinit;
	task_reset;
	load_data2mem;
	#5000 i_start = 1;
	#(PERIOD_50MHZ*2)
	i_start = 0;
	// generate_frame;
	#100
	@ ( posedge clk )
		in_pulse = 1;
	@ ( posedge clk )
		in_pulse = 0;
	task_field_rst;
end

//----------------------模块例化------------------------
wire[15:0]		data00;
wire[15:0]		data01;
wire[15:0]		data02;
wire[15:0]		data10;
wire[15:0]		data11;
wire[15:0]		data12;
wire[15:0]		data20;
wire[15:0]		data21;
wire[15:0]		data22;
wire		  	data_valid;
window_3x3 #(
	.PIX_PER_LINE( 640	),
	.PIX_DOUBLE_LINE( 1280	)
	)inst_window_3x3(
		.clock			( clk			),
		.frame_reset	( field_rst		),
		.datain			( data_tmp		),
		.datain_en		( h_valid		),
		.data00			( data00		),
		.data01			( data01		),
		.data02			( data02		),
		.data10			( data10		),
		.data11			( data11		),
		.data12			( data12		),
		.data20			( data20		),
		.data21			( data21		),
		.data22			( data22		),
		.data_valid		( data_valid	)
						);
   
wire        [1-1:0]          Dvld			;           
wire        [16-1:0]         Data       ;
sobel inst_sobel(
    .i_Clk            ( clk ),
    .i_Rst_n          ( field_rst ),      
    .i_Frame_rst      ( field_rst ),        
    .i_Data_valid     ( data_valid ),        
    .i_Line0_0        ( data00 ),    //previous line
    .i_Line0_1        ( data01 ),        
    .i_Line0_2        ( data02 ),        
    .i_Line1_0        ( data10 ),	//current line
    .i_Line1_1        ( data11 ),        
    .i_Line1_2        ( data12 ),        
    .i_Line2_0        ( data20 ),    //next line    
    .i_Line2_1        ( data21 ),        
    .i_Line2_2        ( data22 ),     
    .o_Dvld			( Dvld ),                  
    .o_Data			( Data )                  
);

//----------------------------------------------
wire [9:0] start_h;
wire [9:0] start_v;
wire [9:0] end_h;
wire [9:0] end_v;

//----------------------系统函数------------------------
//将仿真数据o_data写入外部文件中
integer file_df;
initial begin
	//文件放置在"工程目录\simulation\modelsim"路径下
	file_df = $fopen("post_108.txt");
	if(!file_df)begin
		$display("could not open file!");
		$finish;
	end
end
always @(posedge clk) begin
	if( Dvld )//一帧图像数据
		$fdisplay(file_df,"%d",Data);
end
endmodule
